/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.support.cell;

import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.vos.datalist.ListHeaderVo;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.ListRowVo;
import cn.easyplatform.web.ext.*;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.support.CellCreater;
import cn.easyplatform.web.task.zkex.ComponentBuilderFactory;
import cn.easyplatform.web.task.zkex.ListSupport;
import cn.easyplatform.web.task.zkex.listener.FieldUpdateEventListener;
import cn.easyplatform.web.utils.PageUtils;
import cn.easyplatform.web.utils.WebUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.ext.Disable;
import org.zkoss.zkex.zul.Colorbox;
import org.zkoss.zul.*;
import org.zkoss.zul.impl.FormatInputElement;
import org.zkoss.zul.impl.InputElement;
import org.zkoss.zul.impl.XulElement;

import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public abstract class AbstractCellCreater implements CellCreater {

    protected ListSupport support;

    protected HtmlBasedComponent self;

    public AbstractCellCreater(ListSupport support) {
        this.support = support;
    }

    @Override
    public Component createCell(XulElement anchor, Map<String, Component> managedComponents, ListHeaderVo hv, ListRowVo rv, int index) {
        XulElement lc = createCell();
        //if (!hv.isVisible())
        //     return lc;
        Object value = rv.getData()[index];
        if (hv.isEditable()) {
            if (!Strings.isBlank(hv.getComponent())) {
                if (hv.getInstance() == null) {
                    HtmlBasedComponent newc = createComponent(hv);
                    self = (HtmlBasedComponent) newc.clone();
                    hv.setInstance(newc);
                } else
                    self = (HtmlBasedComponent) ((HtmlBasedComponent) hv.getInstance()).clone();
                self = processComponent(managedComponents, hv, rv, anchor, lc, self, value);
            } else {
                self = PageUtils.createInputComponent(hv.getType());
                bindField(hv, rv, self, anchor, value);
                if (!Strings.isBlank(hv.getEvent())) // 当本身没有定义事件时用列表的事件
                    self.setEvent(hv.getEvent());
                PageUtils.processEventHandler(support, self, anchor);
            }
            if (Strings.isBlank(self.getWidth()))
                self.setHflex("1");
            if (Strings.isBlank(self.getHeight()))
                self.setVflex("1");
            self.setParent(lc);
        } else {
            if (!Strings.isBlank(hv.getComponent())) {
                if (hv.getInstance() == null) {
                    HtmlBasedComponent newc = createComponent(hv);
                    self = (HtmlBasedComponent) newc.clone();
                    hv.setInstance(newc);
                } else
                    self = (HtmlBasedComponent) ((HtmlBasedComponent) hv.getInstance()).clone();
                self = processComponent(managedComponents, hv, rv, anchor, lc, self, value);
                if (self != lc)
                    self.setParent(lc);
            } else {
                if (value != null) {
                    if (!Strings.isBlank(hv.getFormat())) {
                        String format = WebUtils.getFormat(support, hv, rv);
                        setLabel(lc, WebUtils.format(format, value));
                    } else
                        setLabel(lc, value.toString());
                } else
                    setLabel(lc, "");
                self = lc;
                if (!Strings.isBlank(hv.getDraggable()))
                    self.setDraggable(hv.getDraggable());
                if (Strings.isBlank(self.getEvent()) && !Strings.isBlank(hv.getEvent()))
                    self.setEvent(hv.getEvent());
                PageUtils.processEventHandler(support, self, anchor);
            }
        }
        if (!Strings.isBlank(hv.getCellStyle())) {
            if (self.getHflex() != null) {
                if (hv.getCellStyle().indexOf("width") >= 0)
                    self.setHflex(null);
            }
            if (self.getVflex() != null) {
                if (hv.getCellStyle().indexOf("height") >= 0)
                    self.setVflex(null);
            }
            self.setStyle(hv.getCellStyle());
        } else if (hv.getType() == FieldType.INT
                || hv.getType() == FieldType.LONG
                || hv.getType() == FieldType.NUMERIC)
            self.setStyle("text-align:right");
        if (lc.getFirstChild() == null)
            managedComponents.put(hv.getName(), lc);
        else
            managedComponents.put(hv.getName(), lc.getFirstChild());
        return lc;
    }

    /**
     * 处理自定义组件
     *
     * @param managedComponents
     * @param hv
     * @param rv
     * @param anchor
     * @param cell
     * @param comp
     * @param value
     * @return
     */
    protected HtmlBasedComponent processComponent(Map<String, Component> managedComponents, ListHeaderVo hv, ListRowVo rv, XulElement anchor, XulElement cell, HtmlBasedComponent comp, Object value) {
        if (support.getMainHandler().getAccess() != null && !Strings.isBlank(comp.getAccess())) {
            if (!ArrayUtils.contains(support.getMainHandler().getAccess(), comp.getAccess())) {
                if (comp.getAccess().endsWith("_V")) {
                    comp.setVisible(false);
                    return comp;
                } else if (!(comp instanceof EntityExt) && !(comp instanceof ZkExt)) {
                    PageUtils.disabled(comp);
                }
            }
        }
        if (!Strings.isBlank(hv.getEvent())
                && Strings.isBlank(comp.getEvent()))// 当本身没有定义事件时用列表的事件
            comp.setEvent(hv.getEvent());
        if (comp instanceof ZkExt) {
            //设置字典
            if (comp instanceof Queryable)
                ((Queryable) comp).setOptionQuery(hv);
            ComponentBuilderFactory.getZkBuilder(support, (ZkExt) comp, anchor)
                    .build();
            if ((comp instanceof Cacheable) && ((Cacheable) comp).isCache())
                hv.setInstance(comp.clone());
            if (hv.isEditable())
                bindField(hv, rv, comp, anchor, value);//绑定栏位
            else {
                PageUtils.setValue(comp, value, hv.getFormat());
                if (comp instanceof InputElement) {
                    InputElement input = (InputElement) comp;
                    if (cell instanceof Row)
                        comp = new Label(input.getText());
                    else {
                        setLabel(cell, input.getText());
                        comp.detach();
                        comp = cell;
                    }
                }
            }
        } else if (comp instanceof EntityExt) {
            ComponentBuilder builder = ComponentBuilderFactory
                    .getEntityBuilder((OperableHandler) support,
                            (EntityExt) comp, anchor);
            builder.build();
        } else if (comp instanceof Idspace) {
            if (Strings.isBlank(comp.getHeight()))
                comp.setVflex("1");
            if (Strings.isBlank(comp.getWidth()))
                comp.setHflex("1");
            Iterator<Component> itr = comp.queryAll("*").iterator();
            while (itr.hasNext()) {
                Component c = itr.next();
                if (support.getMainHandler().getAccess() != null && !Strings.isBlank(c.getAccess())) {
                    if (!ArrayUtils.contains(support.getMainHandler().getAccess(), c.getAccess())) {
                        if (comp.getAccess().endsWith("_V")) {
                            c.setVisible(false);
                            continue;
                        } else if (!(comp instanceof EntityExt) && !(comp instanceof ZkExt)) {
                            PageUtils.disabled(comp);
                        }
                    }
                }
                if (!Strings.isBlank(c.getId())) {
                    if (c.getId().startsWith("#{") && c.getId().endsWith("}")) {
                        c.setId(StringUtils.substringBetween(c.getId(), "#{", "}"));
                        int len = rv.getData().length;
                        int index = -1;
                        ListHeaderVo lhv = null;
                        for (int i = 0; i < len; i++) {
                            if (support.getEntity().isShowRowNumbers())
                                lhv = support.getHeaders().get(i + 1);
                            else
                                lhv = support.getHeaders().get(i);
                            if (c.getId().equals(lhv.getName())) {
                                index = i;
                                break;
                            }
                        }
                        if (index < 0) {
                            comp.detach();
                            comp.setPage(null);
                            throw new WrongValueException(support.getComponent(), Labels.getLabel("datalist.field.not.exists", new Object[]{hv.getName(), c.getId()}));
                        }
                        if (c instanceof ZkExt) {
                            //设置字典
                            if (c instanceof Queryable)
                                ((Queryable) c).setOptionQuery(hv);
                            c = ComponentBuilderFactory.getZkBuilder(support, (ZkExt) c, anchor)
                                    .build();
                        } else
                            PageUtils.processEventHandler(support, c, anchor);
                        if (lhv.isEditable())
                            bindField(lhv, rv, c, anchor, rv.getData()[index]);
                        else
                            PageUtils.setValue(c, rv.getData()[index]);

                        if (c instanceof InputElement) {
                            InputElement input = (InputElement) c;
                            input.setDisabled(!lhv.isEditable());
                        } else if (c instanceof Checkbox) {
                            Checkbox cbx = (Checkbox) c;
                            cbx.setDisabled(!lhv.isEditable());
                        } else if (c instanceof Disable) {
                            ((Disable) c).setDisabled(!lhv.isEditable());
                        } else if (c instanceof Colorbox)
                            ((Colorbox) c).setDisabled(!lhv.isEditable());

                        managedComponents.put(c.getId(), c);
                    } else if (c instanceof EntityExt) {
                        if (rv.getKeys() != null && rv.getKeys().length > 0)
                            c.setId(Arrays.toString(rv.getKeys()));
                        else
                            c.setId(Arrays.toString(rv.getData()).hashCode() + "");
                        ComponentBuilder builder = ComponentBuilderFactory
                                .getEntityBuilder((OperableHandler) support,
                                        (EntityExt) c, anchor);
                        builder.build();
                    } else {
                        if (c instanceof ZkExt) {
                            //设置字典
                            if (c instanceof Queryable)
                                ((Queryable) c).setOptionQuery(hv);
                            c = ComponentBuilderFactory.getZkBuilder(support, (ZkExt) c, anchor)
                                    .build();
                        } else
                            PageUtils.processEventHandler(support, c, anchor);
                        managedComponents.put(c.getId(), c);
                    }
                } else {
                    if (c instanceof ZkExt) {
                        //设置字典
                        if (c instanceof Queryable)
                            ((Queryable) c).setOptionQuery(hv);
                        ComponentBuilderFactory.getZkBuilder(support, (ZkExt) c, anchor)
                                .build();
                    } else
                        PageUtils.processEventHandler(support, c, anchor);
                    //if (!Strings.isBlank(c.getEvent()))
                    //    PageUtils.addEventListener(support, c, anchor);

                }
            }
        } else if (comp.getFirstChild() != null) {
            Iterator<Component> itr = comp.queryAll("*").iterator();
            List<Component> extList = new ArrayList<>();
            while (itr.hasNext()) {
                Component c = itr.next();
                if (support.getMainHandler().getAccess() != null && !Strings.isBlank(c.getAccess())) {
                    if (!ArrayUtils.contains(support.getMainHandler().getAccess(), c.getAccess())) {
                        if (comp.getAccess().endsWith("_V")) {
                            c.setVisible(false);
                            continue;
                        } else if (!(comp instanceof EntityExt) && !(comp instanceof ZkExt)) {
                            PageUtils.disabled(comp);
                        }
                    }
                }
                if (c instanceof EntityExt || c instanceof ZkExt)
                    extList.add(c);
                else {
                    PageUtils.processEventHandler(support, c, anchor);
                }
            }
            for (Component c : extList) {
                if (c instanceof EntityExt) {
                    ComponentBuilder builder = ComponentBuilderFactory
                            .getEntityBuilder((OperableHandler) support,
                                    (EntityExt) c, anchor);
                    builder.build();
                } else if (c instanceof ZkExt) {
                    //设置字典
                    if (c instanceof Queryable)
                        ((Queryable) c).setOptionQuery(hv);
                    ComponentBuilderFactory.getZkBuilder(support, (ZkExt) c,
                            anchor).build();
                }
            }
            itr = null;
            extList = null;
        } else {
            if (hv.isEditable()) {
                bindField(hv, rv, comp, anchor, value);
            } else {
                if (value != null)
                    PageUtils.setValue(comp, value);
                if (comp instanceof Checkbox)
                    ((Checkbox) comp).setDisabled(true);
                else if (comp instanceof InputElement) {
                    // 如果列表不能编辑的，转成对应的组件：例如:Listcell,Treecell
                    value = ((InputElement) comp).getText();
                    if (cell instanceof Row)
                        comp = new Label(value == null ? "" : value.toString());
                    else {
                        setLabel(cell, value == null ? "" : value.toString());
                        comp.detach();
                        comp = cell;
                    }
                }
            }
            PageUtils.checkAccess(support.getMainHandler().getAccess(), comp);
            if (!Strings.isBlank(hv.getEvent())
                    && Strings.isBlank(comp.getEvent()))// 当本身没有定义事件时用列表的事件
                comp.setEvent(hv.getEvent());
            PageUtils.processEventHandler(support, comp, anchor);
        }
        return comp;
    }

    /**
     * 绑定栏位
     *
     * @param hv
     * @param rv
     * @param comp
     * @param anchor
     * @param value
     */
    protected void bindField(ListHeaderVo hv, ListRowVo rv, Component comp, XulElement anchor, Object value) {
        if (support.getEntity().isInplace()
                && (comp instanceof InputElement))
            ((InputElement) comp).setInplace(true);
        PageUtils.setValue(comp, value);
        if (comp instanceof FormatInputElement) {
            FormatInputElement format = (FormatInputElement) comp;
            if (Strings.isBlank(format.getFormat()))
                format.setFormat(WebUtils.getFormat(support, hv, rv));
        }
        if (FieldUpdateEventListener.add(comp, support,
                hv, rv))
            comp.setAttribute("$f0", hv.getField());//有绑定栏位
    }

    /**
     * 创建自定义组件
     *
     * @param hv
     * @return
     */
    protected HtmlBasedComponent createComponent(ListHeaderVo hv) {
        Span parent = new Span();
        try {
            Executions.createComponentsDirectly(
                    hv.getComponent(), "zul", parent, null);
        } catch (Exception e) {
            parent.detach();
            parent.setPage(null);
            throw new WrongValueException(support.getComponent(), Labels.getLabel("datalist.component.error", new Object[]{hv.getName(), e.getMessage()}));
        }
        HtmlBasedComponent comp = (HtmlBasedComponent) parent.getFirstChild();
        comp.setParent(null);
        parent.detach();
        parent.setPage(null);
        comp.setPage(null);
        return comp;
    }

    /**
     * 创建对应的组件
     *
     * @return
     */
    protected abstract XulElement createCell();

    /**
     * 为组件赋值
     *
     * @param c
     * @param value
     */
    protected abstract void setLabel(XulElement c, String value);
}
